package com.jysz.webcore.ssh.orm.hibernate;

import java.io.Serializable;
import java.util.Date;

import org.hibernate.EmptyInterceptor;
import org.hibernate.type.Type;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.jysz.webcore.ssh.entity.NulEntity;

/**
 * @author kacarich email:javaengine@outlook.com
 *
 */
public class DefaultEntityInterceptor extends EmptyInterceptor {

	private static final long serialVersionUID = 1L;
	private final static Logger LOG = LoggerFactory.getLogger(DefaultEntityInterceptor.class);

	@Override
	public boolean onFlushDirty(Object entity, Serializable id, Object[] currentState, Object[] previousState, String[] propertyNames, Type[] types) {
		if ((entity instanceof NulEntity)) {
			for (int i = 0; i < propertyNames.length; i++) {
				if ("updatedAt".equals(propertyNames[i])) {
					currentState[i] = new Date();
				} else if ("modifier".equals(propertyNames[i])) {
					currentState[i] = ThreadLocalHolder.get();
				}
			}
			return true;
		}
		return false;
	}

	@Override
	public boolean onSave(Object entity, Serializable id, Object[] state, String[] propertyNames, Type[] types) {
		if ((entity instanceof NulEntity)) {
			for (int i = 0; i < propertyNames.length; i++) {
				if (("createdAt".equals(propertyNames[i])) || ("updatedAt".equals(propertyNames[i]))) {
					state[i] = new Date();
				} else if (("creator".equals(propertyNames[i])) || ("modifier".equals(propertyNames[i]))) {
					state[i] = ThreadLocalHolder.get();
				}
			}

			return true;
		}

		return false;
	}
	
	@Override
	public String onPrepareStatement(String sql) {
		while (true) {
			int idx = sql.indexOf("replacetable(");
			if (idx < 0) {
				break;
			}
			int endidx = sql.indexOf(")=1", idx);
			if (endidx < idx) {
				throwError("expected replacetable(srctable, desttable) is true");
			}
			String[] params = sql.substring(idx + 13, endidx).split(",");
			if (params.length != 2) {
				throwError("expected 2 parameters to replacetable(srctable, desttable)");
			}

			String srctable = params[0].trim();
			String desttable = params[1].trim();
			if (srctable.length() == 0 || desttable.length() == 0) {
				throwError("invalid parameters to replacetable(srctable, desttable)");
			}
			String predicate = sql.substring(endidx + 3);
			if (predicate.startsWith(" and ")) {
				predicate = predicate.substring(5);
			}
			
			sql = sql.substring(0, idx) + predicate;
			
			sql = sql.replace(srctable, desttable);
		}
		while (true) {
			// check if function specified
			int idx = sql.indexOf("useindex(");
			if (idx < 0) {
				break;
			}

			// find end of function
			int endidx = sql.indexOf(")=1", idx);
			if (endidx < idx) {
				throwError("expected useindex(table, index) is true");
			}

			// get both parameters
			String[] params = sql.substring(idx + 9, endidx).split(",");
			if (params.length != 2) {
				throwError("expected 2 parameters to useindex(table, index)");
			}

			// trim parameters and verify
			String tableId = params[0].trim();
			String indexHint = params[1].trim();
			if (tableId.length() == 0 || indexHint.length() == 0) {
				throwError("invalid parameters to useindex(table, index)");
			}

			// find actual table name minus id
			int dotIdx = tableId.indexOf('.');
			if (dotIdx < 0) {
				throwError("invalid table name in useindex(table, index)");
			}

			// find table name within declaration
			String tableName = tableId.substring(0, dotIdx);
			int tableIdx = sql.indexOf(" " + tableName + " ");
			if (tableIdx < 0) {
				throwError("unknown table name in useindex(table, index)");
			}

			// remove useindex function from predicate
			String predicate = sql.substring(endidx + 3);
			if (predicate.startsWith(" and ")) {
				predicate = predicate.substring(5);
			}

			// inject use index after table declaration
			sql = sql.substring(0, tableIdx + 2 + tableName.length()) + "use index (" + indexHint + ") " + 
					sql.substring(tableIdx + 2 + tableName.length(), idx) + predicate;
		}
		if(LOG.isDebugEnabled()) {
			LOG.debug("execute sql: "+sql);
		}
		return sql;
	}

	protected void throwError(String message) {
		throw new IllegalStateException(message);
	}
}
